package dfsketches.gr3d;

public class SolidZLinearRenderer extends Renderer {
	
	public SolidZLinearRenderer(int[][] b) {
		bitmap = b;
		h = b.length;
		w = b[0].length;
		zbuffer = new float[h][w];
	}
	
	private void clearZBuffer() {
		final int mx = w-3;
		for (int x = 0; x < h; x++) {
			float[] zbuffer_x = zbuffer[x];
			for (int y = -1; y < mx;) {
				zbuffer_x[++y] = 
				zbuffer_x[++y] = 
				zbuffer_x[++y] = 0f;
			}
			zbuffer_x[w-1] = 0f;
			zbuffer_x[w-2] = 0f;
		}
	}
	
	private void clearBitmap() {
		final int mx = w-3;
		for (int y = 0; y < h; y++) {
			int[] bitmap_y = bitmap[y];
			for (int x = -1; x < mx;) {
				bitmap_y[++x] = 
				bitmap_y[++x] =
				bitmap_y[++x] = 0xFFFFFFFF;
			}
			bitmap_y[w-1] = 0xFFFFFFFF;
			bitmap_y[w-2] = 0xFFFFFFFF;
		}
	}

	public void start() {
		clearZBuffer();
		clearBitmap();
	}
	
	public class Line {
		float x1, x2;
		float y1, y2;
		float z1, z2;
		
		float px1, px2;
		float py1, py2;
		
		float pk;
		
		Line(Vertex v1, Vertex v2) {
			x1 = v1.x;
			y1 = v1.y;
			z1 = v1.z;
			px1 = v1.px;
			py1 = v1.py;
			
			x2 = v2.x;
			y2 = v2.y;
			z2 = v2.z;
			px2 = v2.px;
			py2 = v2.py;
			
			pk = (px2 - px1) / (py2 - py1);
		}
	}
	
	private void renderTrianglePart(int color, int start, int end,
			Line s, Line f) {
		final float dpy_s = s.py2 - s.py1;
		final float dz_s = 1f / s.z2 - 1f / s.z1;
		final float zk_s = dz_s / dpy_s;
		float z_s = 1 / s.z1 + zk_s * (start - s.py1);

		final float dpy_f = f.py2 - f.py1;
		final float dz_f = 1f / f.z2 - 1f / f.z1;
		final float zk_f = dz_f / dpy_f;
		float z_f = 1 / f.z1 + zk_f * (start - f.py1);
		
		final float pk_s = s.pk;
		float px_s = s.px1 + pk_s * (start - s.py1);

		final float pk_f = f.pk;
		float px_f = f.px1 + pk_f * (start - f.py1);
		
		for (int y = start; y < end; y++) {
			float dx = px_f - px_s; 
			float dz = z_f - z_s;
			float kz = dz / dx;
						
			float[] zbuffer_y = zbuffer[y];
			int[] bitmap_y = bitmap[y];

			float z = z_s;
			int px_s_i = (int)px_s;
			if (px_s_i < 0) {
				px_s_i = 0;
				z += -px_s * kz;
			}
			int px_f_i = min((int)px_f, w);
			for (int x = px_s_i; x < px_f_i; x++) {
				if (z > zbuffer_y[x]) {
					zbuffer_y[x] = z;
					bitmap_y[x] = color;
				}
				z += kz;
			}
			
			px_s += pk_s;
			px_f += pk_f;
			
			z_s += zk_s;
			z_f += zk_f;
		}
	}
		
	public void render(Triangle t) {
		Triangle tri = sortTriangle(t);
		
		int color = tri.color;
		
		float aX = tri.a.px;
		float aY = tri.a.py;
		float bX = tri.b.px;
		float bY = tri.b.py;
		float cX = tri.c.px;
		float cY = tri.c.py;
		
		boolean orient = (((bX - aX) * (cY - aY)) >= ((cX - aX) * (bY - aY)));
		//false - АВС против часовой
		
		Line ab = new Line(tri.a, tri.b);
		Line ac = new Line(tri.a, tri.c);
		Line bc = new Line(tri.b, tri.c);
		
		int start_y = max((int)aY, 0);
		int med_y = min(max((int)bY, 0), h);
		int end_y = min((int)cY, h);

		if (orient) {
			if (start_y != med_y)
				renderTrianglePart(color, start_y, med_y, ac, ab);
			if (med_y != end_y)
				renderTrianglePart(color, med_y, end_y, ac, bc);
		} else {
			if (start_y != med_y)
				renderTrianglePart(color, start_y, med_y, ab, ac);
			if (med_y != end_y)
				renderTrianglePart(color, med_y, end_y, bc, ac);
		}
	}
	
	private int min(int a, int b) {
		return (a < b) ? a : b;
	}
	
	private int max(int a, int b) {
		return (a > b) ? a : b;
	}
	
	int[][] bitmap;
	float[][] zbuffer;
	int w;
	int h;
	
}